package com.ljb.nfc_demo.nfc;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.os.Parcelable;
import android.provider.Settings;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.util.Log;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Locale;

/**
 * NFC操作工具类
 * Created by ljb on 2017/7/13.
 */

public class NFCUtils {
    private static final String TAG = "NFCUtils";
    private static NFCUtils nfcUtils;
    private NfcAdapter mNfcAdapter;
    private PendingIntent mPendingIntent;

    /**
     * 获取单例对象
     *
     * @return 单例工具对象
     */
    public static NFCUtils init() {
        if (nfcUtils == null) {
            synchronized (NFCUtils.class) {
                if (nfcUtils == null)
                    nfcUtils = new NFCUtils();
            }
        }
        return nfcUtils;
    }

    /**
     * 判断当前设备是否支持NFC功能
     *
     * @param context 上下文
     * @return 当前设备是否支持NFC功能
     */
    public boolean isSupportNFC(Context context) {
        mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
        return mNfcAdapter != null;
    }

    /**
     * 当前NFC功能是否可用
     *
     * @param context 上下文
     * @return 单钱NFC功能是否可用
     */
    public boolean isEnable(Context context) {
        return isSupportNFC(context) && mNfcAdapter.isEnabled();
    }

    public void initNFC(Activity activity) {
        mPendingIntent = PendingIntent.getActivity(activity, 0, new Intent(activity, activity.getClass()), 0);
    }

    /**
     * 显示NFC设置界面,API16可用
     *
     * @param activity 上下文
     */
    public void showNFCSetting(final Activity activity) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle("NFC")
                .setMessage("请先开启NFC,是否立即开启?")
                .setPositiveButton("设置", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
                            activity.startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
                        } else {
                            activity.startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS));
                        }
                    }
                }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                activity.finish();
            }
        }).show();
    }

    /**
     * 开始监听NFC设备
     */
    public void startNFC(Activity activity) {
        if (mPendingIntent == null) {
            initNFC(activity);
        }
        if (mNfcAdapter != null) {
            mNfcAdapter.enableForegroundDispatch(activity, mPendingIntent, null, null);
        } else {
            if (isSupportNFC(activity)) {
                mNfcAdapter.enableForegroundDispatch(activity, mPendingIntent, null, null);
            }
        }
    }

    /**
     * 停止监听NFC设备
     */
    public void stopNFC(Activity activity) {
        //恢复默认状态
        if (mNfcAdapter != null) {
            mNfcAdapter.disableForegroundDispatch(activity);
            mNfcAdapter = null;
        }
    }

    /**
     * 向NFC标签中写入数据
     *
     * @param data   数据
     * @param intent 意图对象
     */
    public boolean write(String data, Intent intent) {
        if (data == null) return false;
        if (intent == null) return false;

        Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        if (detectedTag == null) return false;

        NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{createTextRecord(data)});
        return writeTag(ndefMessage, detectedTag);
    }

    /**
     * 读取NFC标签中的NDEF数据
     *
     * @param intent 意图对象
     * @return 标签中保存的数据
     */
    public String read(Intent intent) {
        if (intent == null) return null;

        Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        if (detectedTag == null) return null;

        Ndef ndef = Ndef.get(detectedTag);
        if (ndef == null) return null;
        Log.i(TAG, "read: type:" + ndef.getType() + " maxSize:" + ndef.getMaxSize() + "bytes");

        if (TextUtils.equals(NfcAdapter.ACTION_NDEF_DISCOVERED, intent.getAction())) {
            //数据
            Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            return readNFCTag(rawMsgs);
        }
        return null;
    }

    /**************************************一条神奇的分割线*****************************************/


    //读取NFC标签中的数据
    private String readNFCTag(Parcelable[] rawMsgs) {
        NdefMessage msgs[] = null;
        int contentSize = 0;
        if (rawMsgs != null) {
            msgs = new NdefMessage[rawMsgs.length];
            for (int i = 0; i < rawMsgs.length; i++) {
                msgs[i] = (NdefMessage) rawMsgs[i];
                contentSize += msgs[i].toByteArray().length;
            }
        }
        Log.i(TAG, "readNFCTag: NFC标签中的数据长度为:" + contentSize + "bytes");
        try {
            if (msgs != null) {
                NdefRecord record = msgs[0].getRecords()[0];
                return parseTextRecord(record);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //生成NDEF格式的数据
    private NdefRecord createTextRecord(String text) {
        byte[] langBytes = Locale.CHINA.getLanguage().getBytes(Charset.forName("US-ASCII"));
        Charset utfEncoding = Charset.forName("UTF-8");
        //将文本转换为UTF-8格式
        byte[] textBytes = text.getBytes(utfEncoding);
        //设置状态字节编码最高位数为0
        int utfBit = 0;
        //定义状态字节
        char status = (char) (utfBit + langBytes.length);
        byte[] data = new byte[1 + langBytes.length + textBytes.length];
        //设置第一个状态字节，先将状态码转换成字节
        data[0] = (byte) status;
        //设置语言编码，使用数组拷贝方法，从0开始拷贝到data中，拷贝到data的1到langBytes.length的位置
        System.arraycopy(langBytes, 0, data, 1, langBytes.length);
        //设置文本字节，使用数组拷贝方法，从0开始拷贝到data中，拷贝到data的1 + langBytes.length
        //到textBytes.length的位置
        System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
        //通过字节传入NdefRecord对象
        //NdefRecord.RTD_TEXT：传入类型 读写
        NdefRecord ndefRecord = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
        return ndefRecord;
    }

    //向FC标签中写入数据
    private boolean writeTag(NdefMessage message, Tag tag) {
        if (message == null) return false;
        if (tag == null) return false;
        //判断是否为NDEF标签
        Ndef ndef = Ndef.get(tag);
        if (ndef != null) {
            return writeNdefMessage(message, ndef);
        } else {
            return writeNoNdefMessage(message, tag);
        }
    }

    //nfc标签未格式化,或没有分区,先进行格式化,并将数据写入
    private boolean writeNoNdefMessage(NdefMessage message, Tag tag) {
        //Ndef格式类
        NdefFormatable format = NdefFormatable.get(tag);
        //判断标签是否为只读,或允许格式化的
        if (format == null) return false;
        //格式化标签,并写入数据
        try {
            format.connect();
            format.format(message);
            format.close();
            format = null;
            Log.i(TAG, "writeNoNdefMessage: 格式化nfc标签,并向其写入数据成功");
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (FormatException e) {
            e.printStackTrace();
        } finally {
            try {
                if (format != null) {
                    format.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    //nfc标签支持NDEF数据,向nfc标签中写入NDEF数据
    private boolean writeNdefMessage(NdefMessage message, Ndef ndef) {
        //判断是否支持写入
        if (!ndef.isWritable()) return false;
        //判断标签容量是否够用
        int size = message.toByteArray().length;
        if (ndef.getMaxSize() < size) return false;
        //写入数据
        try {
            ndef.connect();
            ndef.writeNdefMessage(message);
            ndef.close();
            ndef = null;
            Log.i(TAG, "writeTag: 向nfc标签写入数据成功");
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (FormatException e) {
            e.printStackTrace();
        } finally {
            try {
                if (ndef != null) {
                    ndef.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }


    //解析NDEF文本数据，从第三个字节开始，后面的文本数据
    private String parseTextRecord(NdefRecord ndefRecord) {
        //判断数据是否为NDEF格式
        if (ndefRecord.getTnf() != NdefRecord.TNF_WELL_KNOWN) {
            return null;
        }
        //判断可变的长度的类型
        if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
            return null;
        }

        try {
            //获得字节数组，然后进行分析
            byte[] payload = ndefRecord.getPayload();
            //下面开始NDEF文本数据第一个字节，状态字节
            //判断文本是基于UTF-8还是UTF-16的，取第一个字节"位与"上16进制的80，16进制的80也就是最高位是1，
            //其他位都是0，所以进行"位与"运算后就会保留最高位
            String textEncoding = ((payload[0] & 0x80) == 0) ? "UTF-8" : "UTF-16";
            //3f最高两位是0，第六位是1，所以进行"位与"运算后获得第六位
            int languageCodeLength = payload[0] & 0x3f;
            //下面开始NDEF文本数据第二个字节，语言编码
            //获得语言编码
            String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
            //下面开始NDEF文本数据后面的字节，解析出文本
            return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
}
